.. _глава_66_start:

alias и with
============

.. index::
    alias
    with

alias
-----
Ключевое слово alias используется для присвоения псевдонимов именам в программе.
Не следует путать alias с alias this, который является другим средством.


**Сокращение длинного имени**

Как мы уже видели в предыдущих главах, некоторые имена становятся слишком длинным и неудобными
Рассмотрим следующую функцию из этой предыдущей главы:

.. code-block:: d

    Stack!(Point!double) randomPoints(size_t count)
    {
        auto points = new Stack!(Point!double);
        // ...
    }


Приходится явно вводить Stack!(Point!double) в нескольких местах в
имеет ряд недостатков:
* Длинные имена могут затруднить чтение кода.
* Ненужно в каждом месте напоминать, что тип - это структура данных Stack
которая содержит объекты двойных инстанций шаблона Point
struct.
* Если требования программы меняются и, например, double нужно заменить
например, double на real, это изменение должно быть выполнено в нескольких местах.
Эти недостатки можно устранить, присвоив новое имя Stack!(Point!double)

.. code-block:: d

    alias Points = Stack!(Point!double);
    // ...
    Points randomPoints(size_t count)
    {
        auto points = new Points;
        // ...
    }

возможно, имеет смысл пойти дальше и определить два псевдонима, один из которых будет использовать преимущества другого:

.. code-block:: d

    alias PrecisePoint = Point!double;
    alias Points = Stack!PrecisePoint;

Синтаксис конструкции alias выглядит следущим образом:

.. code-block::

    alias new_name = existing_name;

После этого определения новое имя и существующее имя становятся синонимами: Они означают одно и то же в программе.
В некоторых программах вы можете встретить более старый синтаксис этой функции:


.. code-block:: d

    / Use of old syntax is discouraged:
    alias existing_name new_name;

alias также полезен при сокращении имен, которые в противном случае должны быть прописаны вместе с именами модулей. Предположим, что имя Queen встречается в
двух различных модулях: chess и palace. Когда оба модуля импортируются,
введение просто Queen приведет к ошибке компиляции:

.. code-block:: d

    import chess;
    import palace;
    // ...
    Queen person; // ← ошибка компиляции


Компилятор не может определить какой из типов Queen следует использовать

.. code-block:: console

    Error: chess.Queen at chess.d(1) conflicts with palace.Queen at palace.d(1

Удобный способ разрешить этот конфликт - назначить псевдонимы одному или нескольким из имен:

.. code-block:: d

    import palace;

    alias PalaceQueen = palace.Queen;
    void main()
    {
        PalaceQueen person;
        // ...
        PalaceQueen anotherPerson;
    }

Псевдоним работает и с другими именами. Следующий код дает новое имя переменной variableWithALongName:

.. code-block:: d

    int variableWithALongName = 42;
    alias var = variableWithALongName;
    var = 43;
    assert(variableWithALongName == 43);

**Гибкость дизайна**

Для гибкости даже такие фундаментальные типы, как int, могут иметь псевдонимы:

.. code-block:: d

    alias CustomerNumber = int;
    alias CompanyName = string;
    // ...
    struct Customer
    {
        CustomerNumber number;
        CompanyName company;
        // ...
    }

Если пользователи этой структуры всегда будут вводить CustomerNumber и CompanyName вместо
вместо int и string, то в будущем конструкцию можно будет в некоторой степени изменить,
не затрагивая пользовательский код.
Это также помогает улучшить читаемость кода. Если тип переменной
CustomerNumber передает больше информации о значении этой переменной
чем int.
Иногда такие псевдонимы типов определяются внутри структур и классов и становятся
частями интерфейсов этих типов. Следующий класс имеет свойство weight:

.. code-block:: d

    class Box
    {
    private:
        double weight_;
    public:
        double weight() const
        {
            alias andwith
                return weight_;
        }
        // ...
    }

Поскольку переменная-член и свойство этого класса определены как double,
пользователям также придется использовать double:

.. code-block:: d

    double totalWeight = 0;
    foreach (box; boxes)
    {
        totalWeight += box.weight;
    }

Давайте сравним его с другой конструкцией, в которой тип веса определяется через alias:

.. code-block:: d


    class Box
    {
    private:
        Weight weight_;
    public:
        alias Weight = double;
        Weight weight() const
        {
            return weight_;
        }
        // ...
    }


Теперь пользовательский код может использовать тип Weight:

.. code-block:: d

    Box.Weight totalWeight = 0;
    foreach (box; boxes)
    {
        totalWeight += box.weight;
    }

При таком дизайне изменение фактического типа Weight в будущем не будет влиять
на пользовательский код. (То есть, если новый тип также поддерживает оператор +=).

**Выявление скрытых имен суперклассов**

Если одно и то же имя встречается и в суперклассе, и в подклассе, то
совпадающие имена в суперклассе скрыты. Даже одного имени в
подклассе достаточно, чтобы скрыть все имена суперкласса, которые соответствуют этому имени.
имя:

.. code-block:: d

    class Super
    {
        void foo(int x)
        {
            // ...
        }
    }

    class Sub : Super
    {
        void foo()
        {
            // ...
        }
    }

    void main()
    {
        auto object = new Sub;
        object.foo(42); // ← compilation ERROR
    }

Поскольку аргументом является 42, значение int, можно ожидать, что для этого будет вызвана функция Super.foo
которая принимает int, будет вызвана для этого использования. Однако, несмотря на то, что
списки параметров у них разные, Sub.foo скрывает Super.foo и вызывает
ошибку компиляции. Компилятор вообще игнорирует Super.foo и сообщает.
что Sub.foo не может быть вызван через int:

.. code-block:: console

    error: function deneme.Sub.foo () is not callable using argument types (int)


Обратите внимание, что это не то же самое, что переопределить функцию суперкласса. Для этого
сигнатуры функций будут одинаковыми, а сама функция будет переопределена
с помощью ключевого слова override. (Ключевое слово override было описано в главе
Наследование (:ref:`Глава Наследование<глава_46_start>`)
Здесь действует не переопределение, а языковая особенность, называемая сокрытием имен. Если бы
не было бы скрытия имен, то функции, которые случайно имеют одинаковое имя foo
которые добавляются или удаляются из этих классов, могли бы молча изменить
которая была бы вызвана. Сокрытие имен предотвращает такие сюрпризы. Это
свойство и других языков ООП.
При желании alias может раскрыть скрытые имена:

.. code-block:: d

    class Super
    {
        void foo(int x)
        {
            // ...
        }
    }

    class Sub : Super
    {
        void foo()
        {
            // ...
        }

        alias foo = Super.foo;
    }

Приведенный выше псевдоним переносит имена foo из суперкласса в интерфейс подкласса
В результате код теперь компилируется и вызывается Super.foo.
Когда это более уместно, можно принести имена под другим
имя:

.. code-block:: d

      class Super
      {
          void foo(int x)
          {
              // ...
          }
      }

      class Sub : Super
      {
          void foo()
          {
              // ...
          }

          alias generalFoo = Super.foo;
      }
      // ...
      void main()
      {
          auto object = new Sub;
          object.generalFoo(42);
      }

Скрытие имен также влияет на переменные-члены. псевдоним также может передавать эти имена в
интерфейс подкласса:

.. code-block:: d

    class Super
    {
        int city;
    }

    class Sub : Super
    {
        string city() const
        {
            return "Kayseri";
        }
    }

Независимо от того, что одна из них является переменной-членом, а другая - функцией-членом, имя city подкласса скрывает имя city суперкласса.

.. code-block:: d

    void main()
    {
        auto object = new Sub;
        object.city = 42; // ← compilation ERROR
    }

Аналогично, имена переменных-членов суперкласса могут быть перенесены в
в интерфейс подкласса с помощью псевдонима, возможно, под другим именем:

.. code-block:: d

    class Super
    {
        int city;
    }

    class Sub : Super
    {
        string city() const
        {
            return "Kayseri";
        }

        alias cityCode = Super.city;
    }

    void main()
    {
        auto object = new Sub;
        object.cityCode = 42;
    }

with
-----

with предназначен для удаления повторяющихся ссылок на объект или символ. Она принимает
выражение или символ в круглых скобках и использует это выражение или символ при
при поиске других символов, которые используются внутри области действия with:

.. code-block:: d

    struct S
    {
        int i;
        int j;
    }

    void main()
    {
        auto s = S();
        with (s)
        {
            i = 1; // means s.i
            j = 2; // means s.j
        }
    }

Можно создать временный объект внутри круглых скобок. В этом случае
временный объект становится lvalue (:ref:`Lvalues и Rvalues<Lvalues и Rvalues>`), время жизни которого заканчивается при
выходом из области видимости:

.. code-block:: d

    with (S())
    {
        i = 1; // the i member of the temporary object
        j = 2; // the j member of the temporary object
    }

Как мы увидим позже в главе "Указатели" (:ref:`Указатели<Указатели>`), можно сконструировать
временный объект с помощью ключевого слова new, и тогда время его жизни может быть
в этом случае время его жизни может быть продлено за пределы области видимости.
Это особенно полезно в разделах case для удаления повторяющихся ссылок
например, на тип enum:

.. code-block::

    enum Color
    {
        red,
        orange
    }
    // ...
    final switch (c) with (Color)
    {
    case red :  // means Color.red
        // ...
    case orange :  // means Color.orange
        // ...
    }

Резюме
------

* alias присваивает псевдонимы существующим именам.
* with удаляет повторяющиеся ссылки на один и тот же объект или символ.
